package com.lai.qq.admin.config.security.login;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lai.qq.admin.config.Constants;
import com.lai.qq.admin.config.security.dto.SecurityUser;
import com.lai.qq.admin.config.security.service.impl.UserDetailsServiceImpl;
import com.lai.qq.admin.modules.system.entity.Role;
import com.lai.qq.admin.modules.system.entity.User;
import com.lai.qq.admin.modules.system.entity.UserRole;
import com.lai.qq.admin.modules.system.mapper.RoleMapper;
import com.lai.qq.admin.modules.system.mapper.UserMapper;
import com.lai.qq.admin.modules.system.mapper.UserRoleMapper;
import com.lai.qq.admin.modules.system.service.IMenuService;
import com.lai.qq.admin.modules.system.service.IRoleService;
import com.lai.qq.admin.utils.Constant;
import com.lai.qq.admin.utils.JwtTokenUtil;
import com.lai.qq.admin.utils.PasswordUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 *  <p> 自定义认证处理 </p>
 *
 */
@Component
@Slf4j
public class AdminAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IMenuService permissionService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取前端表单中输入后返回的用户名、密码
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();
        User user;
        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("username", userName));
        // 判断用户是否存在
        if (!CollectionUtils.isEmpty(userList)) {
            user = userList.get(0);
        } else {
            throw new UsernameNotFoundException("用户名不存在！");
        }
        List<UserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<UserRole>().eq("user_id", user.getId()));
        List<Role> roleList = new LinkedList<>();
        for (UserRole userRole : userRoles) {
            Role role = roleMapper.selectById(userRole.getRoleId());
            roleList.add(role);
        }
        //SecurityUser userInfo = new SecurityUser(user, roleList);

        SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);

        boolean isValid = PasswordUtils.isValidPassword(password, userInfo.getPassword(), userInfo.getCurrentUserInfo().getSalt());
        // 验证密码
        if (!isValid) {
            throw new BadCredentialsException("密码错误！");
        }

        // 前后端分离情况下 处理逻辑...
        // 更新登录令牌
        String token = PasswordUtils.encodePassword(String.valueOf(System.currentTimeMillis()), Constants.SALT);
        // 当前用户所拥有角色代码
        String roleCodes = userInfo.getRoleCodes();
        // 生成jwt访问令牌
        String jwt = Jwts.builder()
                // 用户角色
                .claim(Constants.ROLE_LOGIN, roleCodes)
                // 主题 - 存用户名
                .setSubject(authentication.getName())
                // 过期时间 - 30分钟
                .setExpiration(new Date(System.currentTimeMillis() + 30 ))//* 60 * 1000
                // 加密算法和密钥
                .signWith(SignatureAlgorithm.HS512, Constants.SALT)
                .compact();

        String userId = String.valueOf(userInfo.getCurrentUserInfo().getId());

        Map<String, Object> claims=new HashMap<>();
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(roleList)) {
            for (Role role : roleList) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getCode());
                authorities.add(authority);
            }
        }
        //claims.put(Constant.JWT_authorities,authorities);
        claims.put(Constant.JWT_USER,user);
        claims.put(Constant.JWT_ROLE_LIST,roleList);
        //claims.put(Constant.JWT_ROLES_KEY,roleService.getRoleNames(userId));
        //claims.put(Constant.JWT_PERMISSIONS_KEY,permissionService.getPerms(userId).stream().collect(Collectors.toSet()));
        claims.put(Constant.JWT_USER_NAME,userInfo.getCurrentUserInfo().getUsername());
        String accessToken= jwtTokenUtil.getAccessToken(userId,claims);
        log.info("username={}",userInfo.getCurrentUserInfo().getUsername());
        log.info("系统生成jwt={}",jwt);
        log.info("userId={}",userInfo.getCurrentUserInfo().getId());
        log.info("系统生成访问token={}",token);

        //User user = userMapper.selectById(userInfo.getCurrentUserInfo().getId());
        user.setToken(token);
        userMapper.updateById(user);
        userInfo.getCurrentUserInfo().setToken(accessToken);

        redisTemplate.opsForValue().set(Constant.DILINK_ADMIN_ACCESS_TOKEN + userId, accessToken,30*60,TimeUnit.SECONDS);

        return new UsernamePasswordAuthenticationToken(userInfo, password, userInfo.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
